/* 
 * File:   Entity.h
 * Author: RedEyedKiller
 *
 * Created on 22 Απρίλιος 2010, 12:01 μμ
 */

#ifndef _ENTITY_H
#define	_ENTITY_H

#include "AnimationLogic.h"
#include "Utilities.h"
#include "Vector2.h"
#include <boost/variant.hpp>
#include <string>
#include <list>
#include <map>

class KeyboardEvent;

namespace EntitySystem
{

namespace ScriptPredicates
{
struct ScriptExecutor;
}

namespace components
{
class EntityComponent;
}

class AnimationLogic;
class PhysicsLogic;
class Property;
class ScriptLogic;


/**
 * Entity class contains all the needed information for a game ogject
 * in order to be managed by the Entity manager
 */

class Entity
{
    typedef std::map< std::string, Property* > PropertyMap;

public:
    Entity();

    virtual ~Entity();

    Entity(Entity& org);

    /**
     *  Updates and synchronizes entity's components.
     */

    void Update();

    // <editor-fold defaultstate="collapsed" desc="Set & Get">

    bool IsAlive();
    void Kill();
    void SetID(const std::string& ID);
    void SetID(const char* ID);
    std::string GetID() const;
    Math::Vector2F GetPosition();
    void SetPosition(const Math::Vector2F& pos);
    void SetPosition(float x, float y);
    float GetPositionX();
    float GetPositionY();
    void SetAlive(bool alive);
    void SetType(const std::string& scriptType);
    std::string GetType() const;
    void SetCategory(const std::string& category);
    std::string GetCategory() const;

    /**
     * Returns the width of the entity's draw logic. If the entity has no draw
     * logic returns 0.
     */

    int GetWidth() const;


    /**
     * Returns the height of the entity's draw logic. If the entity has no draw
     * logic returns 0.
     */

    int GetHeight() const;

    /**
     * Sets this entity's physics logic to physicsLogic also sets physicsLogic's parent to this.
     * @param physicsLogic - PhysicsLogic component to be set.
     */
    void SetPhysicsLogic(PhysicsLogic* physicsLogic);
    PhysicsLogic* GetPhysicsLogic() const;

    /**
     * Sets this entity's draw logic to drawLogic also sets drawLogic's parent to this.
     * @param drawLogic - DrawsLogic component to be set.
     */
    void SetAnimationLogic(AnimationLogic* animationLogic);
    AnimationLogic* GetAnimationLogic() const; // </editor-fold>

    // 
    /**
     * Adds x,y to the postion vector.
     * @param x to be added to position.x
     * @param y to be added to position.y
     */

    void AdjustPosition(float x, float y);
    
    /**
     * Sends a string message to current state along with this entity.
     * 
     * @param msg the msg that will be send to current state
     */
    
    void NotifyState(const std::string &msg);

    /**
     * Sets the script object of this Entity
     *
     * @param scriptObject - the script object that controls the entity
     */

    void AddScriptLogic(ScriptLogic *obj);


    /**
     * Removes a script object attached to this Entity by specifying its type.
     *
     * @param scriptObjectType - the type of the script object
     */

    void RemoveScriptLogic(const std::string &scriptObjectType);


    /**
     * Calls the Initialize method for every script attached to this Entity
     */

    void InitializeScripts();


    /**
     * Returns a copy of the internal vector storing the scripts attached to
     * the Entity.
     *
     * @param out: OUTPUT parameter, internal vector is copied here.
     */

    void GetScripts(std::vector< ScriptLogic* > &out) const;


    /**
     * Informs Entity of a keyboard input event.
     * 
     * @param event: the event that we want the Entity to be aware of.
     * @param association: the association described in the keymap.xml for this key event
     */

    void PushKeyboardInput(const KeyboardEvent &event, std::string& association);

    
    /**
     * Creates a property, adds it to this Entity and returns a pointer to it.
     *
     * @param name: name of the property.
     * @return a read-write handle to the property.
     */
    
    Property* AddProperty(const std::string& name);
    
    
    /**
     * Returns a property of this Entity based on its name.
     *
     * @param name: name of the property to find.
     * @return a read-write handle to the requested property.
     * @retval NULL: if the property doesn't exist.
     */
    
    Property* GetProperty(const std::string& name);
    
    
    /**
     * Make all scripts of this entity to call the specified function.
     * @param exec the predicate which will call the function to all scripts
     */
    template<typename T>
    inline void ExecuteOnScripts(const T& exec)
    {
        for_each( scripts.begin( ), scripts.end( ), exec );
    }
    

private:

    /**
     * Finds a script attached to this Entity by the script object's type.
     *
     * @param scriptObjectType - the type of the script object.
     * @return an integer value indicating the index of the script.
     * @retval -1: if script doesn't exist
     */
    int FindScript(const std::string &scriptObjectType);

private:
    /**
     * A unique id for each entity
     */

    std::string ID;

    /**
     * The type of this entity, the key used by the entity creator to create this.
     */

    std::string type;

    /**
     * A property used by script in order to distinguish different groups of entities.
     */

    std::string category;

    bool alive; //if false this entity will not update and will be disposed

    Math::Vector2F position;

    //Basic Logic Components

    AnimationLogic* animationLogic;

    PhysicsLogic* physicsLogic;

    std::vector< ScriptLogic* > scripts;
    
    PropertyMap m_properties;
};

};

#endif	/* _ENTITY_H */
